import { pushTarget, popTarget } from "./dep";
import { nextTick } from "../utils/nextTick";

/**
 * 收集依赖
 * @param {*} vm
 * @param {*} updateComponent 
 * @param {*} cb // 1、渲染watcher 空函数   2、watch的watcher 是handler函数
 * @param {user:Boolean} optins  // user表示用的操作
 */
let id = 0;
class watcher {
  constructor(vm, updateComponent, cb, optins) {
    this.vm = vm;
    this.exprOrfn = updateComponent;
    this.cb = cb;
    this.optins = optins;
    // computed
    this.lazy = optins.lazy; // lazy:true 说明是computed计算属性
    this.dirty = optins.lazy; // dirty 标示用户是否执行
    // 每个组件只有一个watcher
    this.id = id++;
    this.user = !!optins.user;
    // 存放dep
    this.deps = [];
    // dep的id，去重
    this.depsId = new Set();
    // 如果是更新视图函数
    if (typeof this.exprOrfn === 'function') {
      this.getter = this.exprOrfn; // 获取更新视图函数
    } else { // watch字符串
      this.getter = function () {
        let path = this.exprOrfn.split('.');
        let obj = vm;
        for (let i = 0; i < path.length; i++) {
          obj = obj[path[i]]
        }
        return obj;
      }
    }
    // 首次更新 保存watch old_value
    this.value = this.lazy ? void 0 : this.get();
  }
  // 初次渲染
  get() {
    pushTarget(this); // 添加watcher
    let value = this.lazy ? this.getter.call(this.vm) : this.getter(); // 渲染页面
    popTarget(); // 取消watcher
    return value; // watch value
  }
  // 更新
  run() {
    let newValue = this.get(); // watch new_value
    let oldValue = this.value; // watch old_value
    this.value = newValue;
    // user 表示用户的watch
    if (this.user) {
      // cb 是watch的handler
      this.cb.call(this.vm.newValue, oldValue);
    }
  }
  // 更新
  update() {
    if (this.lazy) { // computed
      this.dirty = true;
    } else {
      queueWatcher(this);
    }
  }
  // 添加dep
  addDep(dep) {
    let id = dep.id;
    if (!this.depsId.has(id)) {
      this.deps.push(dep);
      this.depsId.add(id);
      dep.addSub(this);
    }
  }
  evaluate() {
    this.value = this.get();
    this.dirty = false;
  }
  // 相互收集
  depend() {
    // 通过这个wacher找到对应的所有dep
    let i = this.deps.length;
    while (i--) {
      this.deps[i].depend();
    }
  }
}

let queue = [];
let has = {};
let pending = false;


function flushWatcher() {
  queue.forEach(item => {
    item.run();

  });
  queue = [];
  has = {};
  pending = false;
}

function queueWatcher(watcher) {
  let id = watcher.id;
  if (!has[id]) {
    queue.push(watcher);
    has[id] = true;
    if (!pending) {
      // 防抖
      // setTimeout(() => {
      //   queue.forEach(item => item.run());
      //   queue = [];
      //   has = {};
      //   pending = false;
      // }, 0);
      nextTick(flushWatcher);
    }
    pending = true;
  }
}

export default watcher;